//图片懒加载
let viewHeight = window.innerHeight || document.documentElement.clientHeight
let imgs = document.getElementsByTagName('img')
let n = 0 

function lazyLoad() {
	for(let i = n; i < imgs.length; i++) {
		let distance = viewHeight - imgs[i].getBoundingClientRect().top
		if(distance >= 0) {
			imgs[i].src = imgs[i].getAttribute('data-src')
			n = i + 1
		}
	}
}

// 节流
function throttle(fn, interval) {
	let last = 0 
	
	return function() {
		let context = this 
		let args = arguments
		let now = Date.now() 
		
		if(now - last >= interval) {
			fn.apply(context, args)
			last = now
		}
	}
}

// 防抖
function debounce(fn, delay) {
	let timer = null 
	
	return function() {
		let context = this 
		let args = arguments
		
		if(timer) {
			clearTimeout(timer)
		}
		
		timer = setTimeout(function(){
			fn.apply(context, args)
			clearTimeout(timer)
		}, delay)
	}
}

// 有底线的防抖
function throttleDebounce(fn, delay) {
	let last = 0, timer = null 
	
	return function() {
		let context = this 
		let args = arguments
		let now = Date.now() 
		
		if(now - last >= delay) {
			fn.apply(context, args)
			last = now
		} else {
			clearTimeout(timer)
			timer = setTimeout(function() {
				fn.apply(context, args)
				last = now 
				clearTimeout(timer)
			}, delay)
		}
	}
}

// 单例模式
class SingleDog {
	static getInstance() {
		if (!SingleDog.instance) {
			SingleDog.instance = new SingleDog()
		}
		
		return SingleDog.instance
	}
}

// 单例Modal
const Modal = (function() {
	let modal = null 
	
	return function() {
		if (!modal) {
			modal = document.createElement('div')
			modal.id = 'modal'
			modal.innerHTML = '我是弹窗'
			modal.style.display = 'none'
			document.body.appendChild(modal)
		}
		
		return modal
	}
})()

// 深拷贝
function deepClone(obj) {
	if (typeof obj !== 'object' || obj === null) {
		return obj
	}
	
	let copy = {}
	
	if (Array.isArray(obj)) {
		copy = []
	}
	
	for(let key in obj) {
		if (obj.hasOwnProperty(key)) {
			copy[key] = deepClone(obj[key])
		}
	}
	
	return copy
}

// 数组对象去重filter object
function unique(arr) {
	let obj = {}
	
	return arr.filter(item => {
		return !obj[item.id] && (obj[item.id] = 1)
	})
}

// 数组对象去重reduce find
function uniqueReduce(arr) {
	return arr.reduce((pre, cur) => {
		if (!pre.find(item => item.id === cur.id)) {
			pre.push(cur)
		}
		return pre
	}, [])
}

// 千分位 正则
function thousand(num) {
	num = num.toString() 
	let pattern = /(-?\d+)(\d{3})/
	
	while(pattern.test(num)) {
		num = num.replace(pattern, '$1,$2')
	}
	return num
}

// 千分位 for 循环
function thousandFor(num) {
	num = num.toString() 
	let n = 0, result = ''
	
	for(let i = num.length - 1; i >= 0; i--) {
		n++ 
		result = num[i] + result
		
		if (n % 3 === 0 && i !== 0) {
			result = ',' + result 
		}
	}
	
	return result
}


// 手写new 
function myNew(constructor, ...args) {
	let obj = Object.create(constructor.prototye)
	
	let result = constructor.apply(obj, args)
	
	if (typeof result === 'object') {
		return result
	} else {
		return obj
	}
}

// 手写 apply 
Function.prototype.myApply = function(thisObj, args) {
	if (typeof this !== 'function') {
		return new TypeError('not a function')
	}
	
	if (thisObj === null || thisObj === undefined) {
		thisObj = window
	} else {
		thisObj = Object(thisObj)
	}
	
	let fn = Symbol() 
	thisObj[fn] = this 
	let result = thisObj[fn](...args)
	delete thisObj[fn]
	
	return result
}

// 手写 call 
Function.prototype.myCall = function(thisObj, ...args) {
	if (typeof this !== 'function') {
		return new TypeError('not a function')
	}
	
	if (thisObj === null || thisObj === undefined) {
		thisObj = window
	} else {
		thisObj = Object(thisObj)
	}
	
	let fn = Symbol() 
	thisObj[fn] = this 
	let result = thisObj[fn](...args)
	delete thisObj[fn]
	
	return result
}

// 手写 bind 
Function.prototype.myBind = function(thisObj, ...args) {
	if (typeof this !== 'function') {
		return new TypeError('not a function')
	}
	
	let _this = this
	let newBind = function(...secondArgs) {
		let isNew = this instanceof newBind 
		let context = isNew ? this : Object(thisObj)
		
		let result = _this.call(context, ...args, ...secondArgs)
		
		return result
	}
	
	if (_this.prototype) {
		newBind.prototype = Object.create(_this.prototype)
	}
	
	return newBind
}

// 手写 EventEmit 
class EventEmit {
	constructor() {
	  this.handlers = {}
	}
	on(eventName, cb) {
		if (!this.handlers[eventName]) {
			this.handlers[eventName] = []
		}
		this.handlers[eventName].push(cb)
	}
	emit(eventName, ...args) {
		if (this.handlers[eventName]) {
			this.handlers[eventName].forEach(callback => {
				callback(...args)
			})
		}
	}
	off(eventName, cb) {
		let handlers = this.handlers[eventName] 
		if (handlers) {
			let index = handlers.indexOf(cb) 
			if (index !== -1) {
				this.handlers[eventName].splice(index, 1)
			}
		}
	}
	once(eventName, cb) {
		let wrapper = function(...args) {
			cb(...args)
			this.off(eventName, wrapper)
		}
		this.on(eventName, wrapper)
	}
}

Array.prototype.mySort = function(compareFunction) {
	let tempArr = this, len = tempArr.length
  // 如果没有提供比较函数，使用默认的字符串比较
  if (typeof compareFunction !== 'function') {
    compareFunction = (a, b) => {
      const strA = String(a);
      const strB = String(b);
      return strA.localeCompare(strB);
			// if (strA < strB) return -1
			// if (strA > strB) return 1
			// return 0
    };
  }
  
  // 冒泡排序实现
  for (let i = 0; i < len - 1; i++) {
    for (let j = 0; j < len - 1 - i; j++) {
      // 使用比较函数判断元素顺序
      if (compareFunction(tempArr[j], tempArr[j + 1]) > 0) {
        // 交换元素
        [tempArr[j], tempArr[j + 1]] = [tempArr[j + 1], tempArr[j]];
      }
    }
  }
  
  return tempArr;
};

// 手写filter 
Array.prototype.myFilter = function(cb, thisObj) {
	if (typeof cb !== 'function') {
		return new TypeError('not a function')
	}
	if (!Array.isArray(this)) {
		return new TypeError('not a Array')
	}
	
	let tempArr = this, result = [] 
	for(let i = 0; i < tempArr.length;i++) {
		if (cb.call(thisObj, tempArr[i], i, tempArr)) {
			result.push(tempArr[i])
		}
	}
	
	return result
}

// 手写map 
Array.prototype.myMap = function(cb, thisObj) {
	if (typeof cb !== 'function') {
		return new TypeError('not a function')
	}
	if (!Array.isArray(this)) {
		return new TypeError('not a array')
	}
	let tempArr = this, result = []
	
	for(let i = 0; i < tempArr.length; i++) {
		result[i] = cb.call(thisObj, tempArr[i], i, tempArr)
	}
	
	return result
}

// 手写forEach 
Array.prototype.myForEach = function(cb, thisObj) {
	if (typeof cb !== 'function') {
		return new TypeError('not a function')
	}
	
	if (!Array.isArray(this)) {
		return new TypeError('not a array')
	}
	
	let tempArr = this 

	for(let i = 0; i < tempArr.length; i++) {
		cb.call(thisObj, tempArr[i], i, tempArr)
	}
}

// 手写reduce 
Array.prototype.myReduce = function(cb, initialValue) {
	if (typeof cb !== 'function') {
		return new TypeError('not a function')
	}
	
	if (!Array.isArray(this)) {
		return new TypeError('not a array')
	}
	
	let account = initialValue, k = 0, tempArr = this 
	
	if (!account) {
		account = tempArr[k++]
	}
	
	while(k < tempArr.length) {
		account = cb.call(undefined, account, tempArr[k], k, tempArr )
		k++
	}
	// for(; k < tempArr.length; k++) {
	// 	account = cb.call(undefined, account, tempArr[k], k, tempArr)
	// }
	
	return account
}

// 函数柯里化
function curry(fn) {
	return function curried(...args) {
		if (args.length >= fn.length) {
			return fn.apply(this, args)
		} else {
			return function(...secondArgs) {
				return curried.apply(this, args.concat(secondArgs))
			}
		}
	}
}

// 数组扁平化 reduce concat 
function flatten(arr) {
	return arr.reduce((pre, cur) => {
		return pre.concat(Array.isArray(cur) ? flatten(cur) : cur)
	}, [])
}

// 数组扁平化 
let result = []
function flatten2(arr) {
	for(let i = 0; i < arr.length; i++) {
		if (Array.isArray(arr[i])) {
			flatten2(arr[i])
		} else {
			result.push(arr[i])
		}
	}
}

// 手写 instanceof 
function myInstanceof(left, right) {
	left = left.__proto__ 
	
	while(left != null) {
		if (left === right.prototye) {
			return true
		}
		left = left.__proto__
	}
	return false
}

// 手写Object.is 
const is = (x, y) => {
	if (x === y) {
		// +0 和 -0 应该不相等
		return x !== 0 || y !== 0 || 1/x === 1/y
	} else {
		return x !== x && y !== y
	}
}

// 手写Object.assign 
function assign(target, ...args) {
    if (target == null) {
      return new TypeError('Cannot convert undefined or null to object');
    }
    
    // 目标对象需要统一是引用数据类型，若不是会自动转换
    const to = Object(target);

    for (let i = 0; i < args.length; i++) {
      // 每一个源对象
      const nextSource = args[i];
      if (nextSource !== null) {
        // 使用for...in和hasOwnProperty双重判断，确保只拿到本身的属性、方法（不包含继承的）
        for (const nextKey in nextSource) {
          if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
    }
    return to;
}


// Promise 异步加载图片
function loadImageAsync(url) {
	return new Promise(function(resolve, reject) {
		const image = new Image() 
		image.src = url 
		
		image.onload = function() {
			resolve(image)
		}
		
		image.onerror = function(){
			reject(new Error('Could not load image at ' + url))
		}
	})
}

// 手动封装一个JSONP
function jsonp ({url, params, callbackName}) {
	const generateUrl = () => {
		let dataSrc = ''
		for(let key in params) {
			if(params.hasOwnProperty(key)) {
				dataSrc += `${key}=${params[key]}&`
			}
		}
		dataSrc += `callback=${callbackName}`
		return `${url}?${dataSrc}`
	}
	return new Promise((resolve, reject) => {
		const script = document.createElement('script')
		script.src = generateUrl()
		document.body.appendChild(script)
		
		window[callbackName] = data =>{
			resolve(data)
			document.body.removeChild(script)
		}
	})
}


// Promise 对象 实现Ajax 
const getJSON = function(url) {
	return new Promise((resolve, reject) => {
		const xhr = new XMLHttpRequest() 
		xhr.open('GET', url, true) 
		xhr.setRequestHeader('Accept', 'application/json')
		xhr.onreadystatechange = function(){
			// 请求完成
			if (xhr.readyState !== 4) {
				return
			}
			
			if (xhr.status === 200 || xhr.status === 304) {
				resolve(xhr.responseText)
			} else {
				reject(new Error(xhr.responseText))
			}
		}
		xhr.send()
	})
}

// 滚动加载 
window.addEventListener('scroll', function(){
	const clientHeight = document.documentElement.clientHeight 
	const scrollTop = document.documentElement.scrollTop 
	const scrollHeight = document.documentElement.scrollHeight 
	if (clientHeight + scrollTop >= scrollHeight) {
		// 检测到滚动至页面底部，进行后续操作
	}
})

// 将 VirtualDom 转化为真实的DOM结构
/**
 * tag
 * attrs
 * children
 */
// Virtual DOM => DOM 
let virtualDom = {
	tag: 'div',
	attrs: {
		id: 'div',
		class: 'div-class'
	},
	children: [{
		tag: 'div',
		attrs: {
			class: 'div2'
		},
		children: [{
			tag: 'span',
			attrs: {
				class: 'span'
			},
			children: ['Hello world!']
		}]
	}, {
		tag: 'p',
		attrs: {
		},
		children: ['test']
	}]
}

function render(vnode) {
	if (typeof vnode === 'number') {
		vnode = String(vnode);
	}
	if (typeof vnode === 'string') {
		return document.createTextNode(vnode);
	}
	const dom = document.createElement(vnode.tag);
	if (vnode.attrs) {
		Object.keys(vnode.attrs).forEach(key => {
		  const value = vnode.attrs[key];
		  dom.setAttribute(key, value);
		})
	}
	if (vnode.children.length) {
		vnode.children.forEach(child => dom.appendChild(render(child)));
	}
	return dom
}


// 递归阶乘
function factorial(n) {
	if (n === 0) {
		return 1
	}
	return n * factorial(n - 1)
}

// 阶乘尾递归优化
function factorialTail(n, accumulator = 1) {
	if (n === 0) {
		return accumulator
	}
	return factorialTail(n - 1, n * accumulator)
}

// 斐波那契数列
function fibonacci(n) {
	if (n === 0 || n === 1) {
		return n
	}
	return fibonacci(n - 1) + fibonacci(n - 2)
}

// 斐波那契数列 循环代替递归
function fibonacciLoop(n) {
	let a = 0; 
	let b = 1; 
	let result; 
	
	for (let i = 2; i <= n; i++) {
		result = a + b 
		a = b 
		b = result
	}
	
	return result
}

// 斐波那契数列 记忆化优化
const memo = {}
function fibonacciMemo(n) {
	if (n === 0 || n === 1) {
		return n
	}
	if (memo[n]) {
		return memo[n]
	}
	const result = fibonacciMemo(n - 1) + fibonacciMemo(n - 2)
	memo[n] = result 
	return result 
}

/**
 * *****************************************
 * *****************************************
 * 构造函数重写Promise
 * let p = new Promise((resolve, reject) => {
 * 		resolve('OK') // resolve, reject, throw 都可以改变Promise 的状态
 * 		reject('error')
 * 		throw 'error'
 * })
 * p.then(value => {
 * 		console.log(value)
 * }, reason => {
 * 		console.wran(reason)
 * })
 */
// 声明构造函数
class Promise {
	// 构造方法
	constructor(executor) {
		// 添加属性
		this.PromiseState = 'pending'
		this.PromiseResult = null 
		// 声明属性
		this.callbacks = []
		// 保存实例对象的 this 的值
		const self = this 
		// resolve 函数
		function resolve(data) {
			// 判断状态
			if(self.PromiseState !== 'pending') {
				return
			}
			// 1.修改对象的状态（promiseState）
			self.PromiseState = 'fulfilled'
			// 2.设置对象的结果值（promiseResult)
			self.PromiseResult = data
			// 调用成功的回调函数
			setTimeout(() => {
				self.callbacks.forEach(item => {
					item.onResolved(data)
				})
			})
		}
		// reject
		function reject(data) {
			// 判断状态
			if (self.PromiseState !== 'pending') {
				return
			}
			// 1.修改对象的状态（promiseState）
			self.PromiseState = 'rejected'
			// 2.设置对象的结果值（promiseResult)
			self.PromiseResult = data
			// 调用失败的回调函数
			setTimeout(() => {
				self.callbacks.forEach(item => {
					item.onRejected(data)
				})
			})
		}

		try {
			// 同步调用【执行器函数】
			executor(resolve, reject)
		} catch(e) {
			// 修改 promise 对象状态为 【失败】
			reject(e)
		}
	}

	// then 方法封装
	then(onResolved, onRejected) {
		const self = this
		// 判断回调函数参数
		if (typeof onRejected !== 'function') {
			onRejected = reason => {
				throw reason
			}
		}
		if (typeof onResolved !== 'function') {
			onResolved = value => value
		}
		return new Promise((resolve, reject) => {   
			// 调用回调函数 PromiseState 
			function callback(type) {
				try {
					// 获取回调函数的执行结果
					let result = type(self.PromiseResult)
					// 判断
					if (result instanceof Promise) {
						//如果是 Promise 类型的对象
						result.then(v => {
							resolve(v)
						}, r => {
							reject(r)
						})
					} else {
						// 结果的对象状态为成功
						resolve(result)
					}
				} catch (e) {
					reject(e)
				}
			}
			if (this.PromiseState === 'fulfilled') {
				setTimeout(() => {
					callback(onResolved)
				}) 
			}
			if (this.PromiseState === 'rejected') {
				setTimeout(() => {
					callback(onRejected)
				})
			}
			if (this.PromiseState === 'pending') {
				// 保存回调函数
				this.callbacks.push({
					onResolved: function() {
						callback(onResolved)
					},
					onRejected: function() {
						callback(onRejected)
					}
				})
			}
		})
	}

	// catch 方法封装
	catch(onRejected) {
		return this.then(undefined, onRejected)
	}

	finally(callback) {
		return this.then(
			// onFulfilled 处理函数
			value => Promise.resolve(callback()).then(() => value),
			// onRejected 处理函数
			reason => Promise.resolve(callback()).then(() => {throw reason})
		)
	}

	// 添加 resolve 方法
	static resolve(value) {
		// 返回promise对象
		return new Promise((resolve, reject) => {
			if (value instanceof Promise) {
				value.then(v => {
					resolve(v)
				}, r => {
					reject(r)
				})
			} else {
				// 状态设置成功
				resolve(value)
			}
		})
	}

	// 添加 reject 方法
	static reject(reason) {
		return new Promise((resolve, reject) => {
			reject(reason)
		})
	}

	// 添加 all 方法
	static all(promises) {
		// 返回结果为promise对象
		return new Promise((resolve, reject) => {
			// 声明变量
			let count = 0 
			let arr = []
			// 遍历
			for(let i = 0; i < promises.length;i++) {
				promises[i].then(v => {
					// 得知对象的状态是成功
					// 每个promise对象都成功
					count++ 
					// 将当前promise对象成功的结果 存入到数组中
					arr[i] = v 
					// 判断
					if (count === promises.length) {
						// 修改状态
						resolve(arr)
					}
				}, r => {
					reject(r)
				})
			}
		})
	} 

	// 添加 race 方法
	static race(promises) {
		return new Promise((resolve, reject) => {
			for(let i = 0; i < promises.length;i++) {
				promises[i].then(v => {
					// 修改返回对象的状态为【成功】
					resolve(v)
				}, r => {
					// 修改返回对象的状态为【失败】
					reject(r)
				})
			}
		})
	}
}



 
// 实现有并行限制的Promise调度器
class Scheduler {
	constructor() {
	  this.queue = []
		this.maxCount = 2 
		this.runCounts = 0
	}
	add(promiseCreator) {
		this.queue.push(promiseCreator)
	}
	request() {
		if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
			return
		}
		this.runCounts++
		
		this.queue.shift()().then(() => {
			this.runCounts--
			this.request()
		})
	}
	taskStart() {
		for(let i = 0; i < this.maxCount; i++) {
			this.request()
		}
	}
}
const timeout = time => new Promise(resolve => {
	setTimeout(resolve, time)
})
const scheduler = new Scheduler() 
const addTask = (time, order) => {
	scheduler.add(() => timeout(time).then(() => console.log(order)))
}
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
scheduler.taskStart()

// 冒泡排序
let arr = [5, 4, 3, 2, 1]  // 4 3 2 1 5
function bubbleSort(arr) {
	// 轮数
	for(let i = 0; i < arr.length - 1; i++) {
		// 次数
		for(let j = 0; j < arr.length - 1 - i; j++) {
			// 判断前一个大于后一个数时进行交换
			if(arr[j] > arr[j+1]) {
				// 借助第三方变量交换两个变量的值
				let temp = arr[j]
				arr[j] = arr[j+1]
				arr[j+ 1] = temp
			}
		} 
	}
	return arr
}

// 数组反转
function arrReverse(arr) {
	for (let i = 0; i < arr.length/2;i++) {
    let temp = arr[i]
    arr[i] = arr[arr.length - 1 - i]
    arr[arr.length - 1 - i] = temp
	}
	return arr
}

// 模拟打印机打字效果
let str = '1233232粉色的防晒服42324232423fdf离开健身房斯库拉多夫健康23232323232323是奉公守法介绍了苦尽甘来开始就高考了'
let index = 0
function printConsole() {
	// if (str.length - 1 === index) {
	// 	return
	// }
	// setTimeout(() => {
	// 	console.log(str[index])
	// 	index++
	// 	printConsole()
	// }, 500)
	let timer = setInterval(() => {
		console.log(str[index])
		index++

		if (index === str.length) {
			clearInterval(timer)
		}
	}, 500)
}

// 让 for of 可以遍历对象
var myObject = { a: 1, b: 2, c: 3 };
myObject[Symbol.iterator] = function(){
	const _this = this
	const keys = Object.keys(this)
	let index = 0
	return {
		next(){
        	return {
	        	value: _this[keys[index++]],
	        	done: index>keys.length
        	}
        }
    }
}
// 标准写法
Object.defineProperty( myObject, Symbol.iterator, {
	enumerable: false,
	writable: false,
	configurable: true,
	value: function() {
		const _this = this
	    //也可使用: keys = Object.getOwnPropertyNames(this)
	    const keys = Object.keys(this)
	    let index = 0
		return {
			next(){
	        	return {
		        	value: _this[keys[index++]],
		        	done: index>keys.length
	        	}
	        }
	    }
	}
});
//首先创建一个基于对象原型扩展的iterable,并给它添加一个迭代器
const iterable = Object.create(Object.prototype,{
	[Symbol.iterator]:  {
        enumerable: false,
        writable: false,
        configurable: true,
        value: function() {
            const _this = this
            //也可使用: keys = Object.getOwnPropertyNames(this)
            const keys = Object.keys(this)
            let index = 0
            return {
                next(){
                    return {
                        value: _this[keys[index++]],
                        done: index>keys.length
                    }
                }
            }
		}
	}
})
let myObject = { a: 1, b: 2, c: 3 };

//替换myObject的原型, 使myObject可迭代
//为了不丢失对象myObject原有的原型中的东西
//iterable在创建时将原型设为了Object.prototype
Object.setPrototypeOf(myObject,iterable)
for(let item of myObject){
  console.log(item)
}

//定义一个函数用于给obj添加迭代器
function iterable(obj){
    if(Object.prototype.toString.call(obj) !== "[object Object]"){
    	return //非对象，不处理
	}
	if(obj[Symbol.iterator]){
		return //避免重复添加
	}
	const it = Object.create(Object.getPrototypeOf(obj), {
		[Symbol.iterator]:  {
	        enumerable: false,
	        writable: false,
	        configurable: true,
	        value: function() {
	            const _this = this
	            //也可使用: keys = Object.getOwnPropertyNames(this)
	            const keys = Object.keys(this)
	            let index = 0
	            return {
	                next(){
	                    return {
	                        value: _this[keys[index++]],
	                        done: index>keys.length
	                    }
	                }
	            }
			}
		}
	})
	Object.setPrototypeOf(obj, it)
}




// 无重复字符的最长子串
// 给定一个字符串，请你找出其中不含有重复字符的 最长子串 的长度。
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
  // 滑动窗口初始化为一个空数组
  let arr = [];
  // 要返回的字符串的长度
  let max = 0;
  for (let i = 0; i < s.length; i++) {
    // 使用 indexOf 判断是否在数组中出现过
    let index = arr.indexOf(s[i])
    // 如果出现过
    if (index !== -1) {
      // 从数组开头到当前字符串全部截取掉
      arr.splice(0, index + 1);
    }
    // 在窗口右边放进新的字符
    arr.push(s.charAt(i));//charAt(i)返回指定位置的字符
    // 更新下最大值
    max = Math.max(arr.length, max);//两个数比较
  }
  // 返回
  return max;
};




